Skip to content

Propagate only what changed, and replace a directory with a symlink - #62

Open
woksin wants to merge 1 commit into
mainfrom
fix/propagate-only-changed-and-resolve-symlink-conflicts
Open

Propagate only what changed, and replace a directory with a symlink#62
woksin wants to merge 1 commit into
mainfrom
fix/propagate-only-changed-and-resolve-symlink-conflicts

Conversation

@woksin

@woksin woksin commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Fixed

Propagation has failed on every run since 2026-07-03, for two unrelated
reasons that hit different repositories - which is why the failing subset
kept changing size and looked flaky.

A path the source ships as a symlink cannot simply gain a blob where the
target still holds a directory: the tree API answers GitRPC::BadObjectState.
Six targets have .github/instructions as a real directory. Converting one
has to be expressed as a delete plus an add, so the tree now carries the
delete first.

The other five hit the secondary rate limit. The up-to-date check stopped
at the first file that differed and then a blob was created for every
propagated file - 63 symlinks plus the rest, to every one of ~26 targets,
on every run. The check now keeps what it learned and only the files that
actually differ are uploaded, which is usually one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JfEAq3cyHk3mrWpo8QA9db
@woksin woksin added the patch label Aug 5, 2026
@woksin

woksin commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

Reviewer notes.

How the two causes were separated. The failing set is 11 repositories. Comparing all 63 source symlink paths against each target's trees splits them cleanly:

Cause Repos Evidence
.github/instructions is a tree in the target, a 120000 blob in the source Fundamentals, cli, Ante, Chronicle.TypeScript, Chronicle.Elixir, Chronicle.Kotlin the only tree/blob conflict any of them has
secondary rate limit Specifications, Workshops, Architecture, Prologue, Eventmodelers-Build-Kit no conflict at all; the Architecture job's actual error is Could not create blob for .agents/PROJECT.md / rate limit hit

Arc, Studio and Lens have no conflict and are not failing. Arc was failing in earlier runs and dropped out once its .github/instructions was converted — that is the shifting-subset behaviour the issue reported, explained.

Note .claude/commands is a tree in the source too (it holds per-file symlinks), so a target having it as a tree is correct. It looks like a conflict and is not; the comparison excludes it by construction because it compares against source paths whose mode is 120000.

Volume, concretely. Before: every propagated file uploaded to every target on every run. After: only the files whose blob sha or mode differs. On a typical run that is one file rather than 63-plus, per repository, which is what puts the rate limit out of reach rather than merely further away.

What I verified, and how. The step-3/step-4 logic was extracted and run against a simulated tree where one file matches, one differs, and one path is a directory in the target. It selected exactly the two differing files, flagged only the directory, and emitted {"path":".github/instructions","mode":"040000","type":"tree","sha":null} ahead of the additions. bash -n is clean.

What I could not verify. I have not run this against the real API. Two things to watch on the first run:

  1. That the tree API accepts a sha: null delete and an add for the same path in one call. It is the documented way to delete an entry under a base_tree, and the delete is ordered first, but same-path delete-then-add in a single tree is the part I am inferring rather than observing.
  2. That .agents/PROJECT.md — the file the rate-limited jobs died on — is genuinely per-repository content and not something propagation should be overwriting at all. It is the one propagated path whose name suggests it is project-local, and general.md describes .agents/PROJECT.md as living in the consuming project. Worth a look independently of this fix; I have not changed its handling.

If (1) turns out not to work, the fallback is two tree calls — delete, then add on the resulting tree — which costs one extra request per affected repository and only on the conversion run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant